/* Filename: ./4-服务器.c
 * Description:
 * Last modified: humble 2020-11-12 00:27:27
 * $ gcc -Wall 4-服务器.c -o 4-服务器 -levent
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <event.h>
#include <event2/listener.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void event_cb(struct bufferevent *bev, short what, void *ctx)
{
    if (what & BEV_EVENT_EOF) {
        printf("客户端下线\n");
        bufferevent_free(bev); //释放bufferevent对象
    }
}

void read_cb(struct bufferevent *bev, void *ctx)
{
    char buf[128] = {0};
    size_t ret = bufferevent_read(bev, buf, sizeof(buf));
    if (ret < 0) { fprintf(stderr, "bufferevent_read() failed"); }
    else { printf("read len %lu %s\n", ret, buf); }
}

void listen_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)
{
    struct event_base *base = (struct event_base *)arg;
    printf("接受%d的连接\n", fd);
    //针对已存在的socket创建bufferevent对象
    //base:事件集合,
    //fd:已存在的socketfd
    //options:BEV_OPT_CLOSE_ON_FREE(如果释放bufferevent对象则自动帮我们关闭连接)
    //bufferevent_socket_new(struct event_base *base, int fd, int options)
    struct bufferevent * bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if (bev == NULL) { fprintf(stderr, "bufferevent_socket_new() failed"); exit(1); }

    //给bufferevent设置回调函数，有点像select
    //bufev:
    //readcb:当可读时回调该函数
    //writecb:当可写时回调该函数
    //eventcb:当有异常时间发生时回调该函数
    //cbarg:回调函数被调用时传的参数
    //bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg)
    bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);

    //使能bufferevent对象
    bufferevent_enable(bev, EV_READ);
}

int main(int argc, char **argv)
{
    if(argc != 1){ fprintf(stderr, "argc !=1"); exit(1); }

    //初始化事件集合
#if 0
    event_init();
#else
    //或者用event_base_new();那就需要接收返回值并且自己free
    struct event_base *base = event_base_new();
    if (base == NULL) { fprintf(stderr, "event_base_new() failed"); exit(1); }
#endif

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8000);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    //evconnlistener_new_bind:socket();bind();listener();accept();
    //因为该函数已经包括了accept(),所以我们只需在回调函数内读或写即可
    //创建监听对象，在指定的地址上监听接下来的TCP连接
    //(事件集合,当有连接时调用的函数,回调函数参数,
    //释放监听对象关闭socket|端口重复使用,监听队列长度,
    //绑定信息,绑定信息长度)
    struct evconnlistener *listener = evconnlistener_new_bind(base, listen_cb, base,
            LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 10,
            (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (listener == NULL) { fprintf(stderr, "evconnlistener_new_bind() failed"); exit(1); }



    //开始监听//死循环监听 如果集合中没有事件可以监听，则返回
    event_base_dispatch(base);

    //释放监听对象listener
    evconnlistener_free(listener);

    //释放集合
    event_base_free(base);

    return 0;
}
